#pragma once
#ifndef __FORWARD_TABLE_H__
#define __FORWARD_TABLE_H__

#include <stdint.h>
#include <unordered_map>
#include <util/forward_rule.h>
#include <app/VirtualServer.h>
#include <app/TrueClient.h>



template<>
struct std::hash<forward_rule>{
    size_t operator()(forward_rule const& f) const noexcept{
        constexpr uint8_t MARK = 0b0011'1111;
        uint32_t code = ((f.src_port & MARK) << 24) 
                      | ((f.dst_user & MARK) << 18)
                      | ((f.dst_host & MARK) << 12)
                      | ((f.target_ip & MARK) << 6)
                      | (f.target_port & MARK);
        return std::hash<uint32_t>()(code);
    }
};

struct forward_rule_app{
    std::shared_ptr<VirtualServer> virtual_server;
    std::vector<std::shared_ptr<TrueClient>> clients;
};

struct forward_table{
    std::unordered_map<forward_rule, uint32_t> rule_to_key;
    std::unordered_map<uint32_t, forward_rule> key_to_rule;
    std::unordered_map<uint32_t, forward_rule_app> apps;

    forward_table() = default;

    bool contain_rule(forward_rule const& rule){
        return rule_to_key.count(rule);
    }

    bool contain_key(uint32_t key){
        return key_to_rule.count(key);
    }

    forward_rule get_rule(uint32_t key){
        return key_to_rule.at(key);
    }

    uint32_t get_key(forward_rule const& rule){
        return rule_to_key.at(rule);
    }

    uint32_t put_rule(forward_rule const& rule){
        uint32_t k = gen_key();
        rule_to_key.emplace(rule, k);
        key_to_rule.emplace(k, rule);
        return k;
    }

    void rm_rule(forward_rule const& rule){
        uint32_t k = get_key(rule);
        rm_rule(k);
    }

    void rm_rule(uint32_t k){
        forward_rule rule = get_rule(k);
        rule_to_key.erase(rule);
        key_to_rule.erase(k);
        apps.erase(k);
    }

    forward_rule_app get_apps(uint32_t k){
        return apps.at(k);
    }

    void add_apps(uint32_t k, std::shared_ptr<VirtualServer> server){
        apps[k].virtual_server = server;
    }

    void add_apps(uint32_t k, std::shared_ptr<TrueClient> client){
        apps[k].clients.push_back(client);
    }
private:
    uint32_t gen_key(){
        while (contain_key(key))
            key++;
        return key++;
    }
private:
    uint32_t key;
};

#endif // __FORWARD_TABLE_H__